using System;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;

namespace XTCast.Radio
{
	
	/// <summary>The Station creates a DJ thread, then listens on a particular port and accepts
	/// incoming connections, dispatching each one to its own Transmitter thread</summary>
	public class Station:IDisposable {

		
		/// <summary>Creates a Station and starts its thread waiting for incoming connections.</summary>
		/// <param name="props">The Properties read from the station's configuration file.</param>
		public Station(StationConfig sc):base() {
			this.sc = sc;
			InitMutex(sc.ID);
			bm = new BufferManager(this,Radio.Config.Buffers);
			dj = (DJ)System.Activator.CreateInstance(System.Type.GetType("XTCast.Radio."+sc.DJType), new object[]{this,bm,sc});
			if (dj == null) dj = new FileDJ(this,bm,sc);
		}

		private void InitMutex(string name) {
			try {
				stationMutex = new System.Threading.Mutex(false,name);
			} catch (Exception ex) {
				Log.Error(this,ex);
			}
		}

		private StationConfig sc;
		virtual public StationConfig StationConfig {
			get {
				return sc;
			}
		}

		virtual public System.String ID {
			get {
				return StationConfig.ID;
			}	
		}

		virtual public int MaxTransmitters {
			get {
				return maxTransmitters;
			}
		}

		/// <summary>Returns an array of all active Transmitters of this Station</summary>
		virtual public Transmitter[] Transmitters {
			get {
				lock (fTransmitters.SyncRoot) {
					return (Transmitter[])fTransmitters.ToArray(typeof(Transmitter));
				}
			}	
		}

		virtual public bool Active {
			get {
				lock (fTransmitters.SyncRoot) {
					foreach (Transmitter t in fTransmitters) {
						if (!t.Paused) {
							lastActive = DateTime.Now;
							return true;
						}
					}
					return false;
				}
			}
		}

		DateTime lastActive = DateTime.Now;
		public DateTime LastActive{
			get { return lastActive; }
		}

		public TimeSpan SinceActive {
			get { return DateTime.Now.Subtract(lastActive); }
		}

		virtual public DJ DJ {
			get {
				return dj;
			}
		}

		virtual public BufferManager BufferManager {
			get {
				return bm;
			}
		}

		PlaylistFilter _playlist; 
		virtual public PlaylistFilter PlaylistFilter {
			get {
				return _playlist;
			}	
			set {
				_playlist = value;
			}
		}

		IMediaLibrary _library; 
		virtual public IMediaLibrary MusicLibrary {
			get {
				return _library;
			}	
			set {
				_library = value;
			}
		}

		virtual public float IdleSeconds {
			get {
				if (Active)
					return - 1;
				else {
					return ((System.DateTime.Now).Ticks - fLastTransDisconnected.Ticks) / 1000;
				}
			}
		}

		public override System.String ToString() {
			return String.Format("RS{0,13:G}", ID);
		}

		public void Start() {
			if (!Run) {
				Run = true;
				if (!stationMutex.WaitOne(TimeSpan.FromSeconds(10),false)) {
					Log.Warn(this,"Already running on a different Application Domain");
				}
				dj.Start();
			}
		}

		public void Stop() {
			if (Run) {
				Run = false;
				Thread.Sleep(1000);
				stationMutex.ReleaseMutex();
			}
		}
		internal virtual void  TransmitterStopped(Transmitter trans) {
			fTransmitters.Remove(trans);
			if (Transmitters.Length == 0) fLastTransDisconnected = System.DateTime.Now;
			TransmitterNotifyCount();
		}

		internal virtual void TransmitterStarted(Transmitter trans) {
			fTransmitters.Add(trans);
			TransmitterNotifyCount();
		}

		void TransmitterNotifyCount() {
			call++;
			System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(TransmitterNotify),call);
		}

		private int lastReported, maxTransmitters, call;
		void TransmitterNotify(object o) {
			Thread.Sleep(3000);
			if (call==(int)o) {
				maxTransmitters = System.Math.Max(maxTransmitters, Transmitters.Length);
				if (lastReported>Transmitters.Length)
					Log.WriteLine(this,String.Format("Down to {0} listeners (max listeners {1})", Transmitters.Length, MaxTransmitters));
				else if (lastReported<Transmitters.Length)
					Log.WriteLine(this,String.Format("Up to {0} listeners (max listeners {1})",Transmitters.Length, MaxTransmitters));
				lastReported=Transmitters.Length;
			}
		}
		
		public void Dispose() {
			Stop();
		}

		public class TrackEventArgs : EventArgs {
			Track track;
			public TrackEventArgs( Track track ) {
				this.track = track;
			}
			public Track Track {
				get { return track; }
			}
		}

		public delegate void TrackEventDelegate(Object sender, TrackEventArgs e);
		public event TrackEventDelegate TrackChanged;
		public event TrackEventDelegate TrackError;

		public virtual void OnTrackChanged(Track track) {
			TrackChanged(this,new TrackEventArgs(track));
		}

		public virtual void OnTrackError(Track track) {
			TrackError(this,new TrackEventArgs(track));
		}
		
		public bool Run;
		private DJ dj;
		private BufferManager bm;
		private System.Collections.ArrayList fTransmitters = new System.Collections.ArrayList(10);
		private Station fLinkedStation;
		private System.DateTime fLastTransDisconnected;
		private Mutex stationMutex;
	}
}